<?php

class LightJob_LightJob implements LightJob_ILightJob 
{
	private $conf;
	
	/**
	 * The logger
	 *
	 * @var LightJob_ILogger
	 */
	private $logger;
	
	/**
	 * The backend
	 *
	 * @var LightJob_IBackend
	 */
	private $backend;
	
	/**
	 * The daemon
	 *
	 * @var LightJob_IDaemon
	 */
	private $daemon;
	
	/**
	 * The runner
	 *
	 * @var LightJob_IRunner
	 */
	private $runner;
	
	public function __construct()
	{
		$this->conf = null;
		$this->logger = null;
		$this->backend = null;
		$this->daemon = null;
		$this->runner = null;
	}
	
	public function setProperty($key,$value)
	{
		@$this->conf[$key] = $value;
	}
	
	public function getProperty($key,$default=null)
	{
		if(!@array_key_exists($key,$this->conf))
		{
			return $default;
		}
		return $this->conf[$key];
	}

    /**
     * Configures the Light job with the specified configuration settings
     *
     * @param  mixed     $conf filename or array
     * @return LightJob  $this
     * @throws Exception if an error occured
     */
    public function configure($confOrig)
    {
    	$conf = $confOrig;
        if($this->conf !== null) throw new Exception("Daemon is already configured");

        if(is_string($conf)) {
            // file mode
            if(!array_key_exists('LIGHTJOB_ENV',$_SERVER)) throw new Exception("Environment variable 'LIGHTJOB_ENV' is not set");
            if(!file_exists($conf)) throw new Exception("Configuration file '$conf' does not exist are is not readable");
            $conf = @parse_ini_file($conf,true);
            if($conf===null || !is_array($conf) || count($conf)===0) throw new Exception("Configuration file '$confOrig' is empty of contains non valid data");
            if(!array_key_exists($_SERVER['LIGHTJOB_ENV'],$conf)) throw new Exception("Configuration file '$confOrig' does not contain the section '{$_SERVER['LIGHTJOB_ENV']}'");
            $this->conf = $conf[$_SERVER['LIGHTJOB_ENV']];
        }elseif(is_array($conf)){
            $this->conf = $conf;
        }else{
        	$this->conf = array();
        }
        // logger
        if(!array_key_exists('logger',$this->conf)) {
        	$this->conf['logger'] = 'file';
        }
        if($this->conf['logger']==='file' && !array_key_exists('logger.file.file',$this->conf))
        {
        	$this->conf['logger.file.file'] = dirname(__FILE__).'/../../logs/'.str_replace('.','-',$_SERVER['LIGHTJOB_ENV']).'.log';
        }
            // checks if logger is a named logger (no class name)
            // try to add prefix to see if class exists
            $class = "LightJob_Logger_".ucfirst($this->conf['logger']);
            $file = dirname(__FILE__).'/../'.str_replace("_","/",$class).".php";
            if(is_file($file)){
                // exists
                require_once($file);
            }else{
                $class = $this->conf['logger'];
                $file = str_replace("_","/",$class).".php";
                if(is_file($file)){
                    // exists
                    require_once($file);
                }else{
                    throw new Exception("Unable to load logger '{$this->conf['logger']}'");
                }
            }
            if(!class_exists($class)) {
                throw new Exception("Logger file '$file' does not contains valid logger class '$class'");
            }
            $this->setLogger(new $class);
        
        if(!array_key_exists('database.params.host',$this->conf)) $this->conf['database.params.host'] = 'localhost';
        if(!array_key_exists('database.params.username',$this->conf)) $this->conf['database.params.username'] = 'lightjob';
        if(!array_key_exists('database.params.password',$this->conf)) $this->conf['database.params.password'] = 'lightjob';
        if(!array_key_exists('database.params.dbname',$this->conf)) $this->conf['database.params.dbname'] = 'lightjob';
        
        //backend
        if(!array_key_exists('backend',$this->conf)) {
        	$this->conf['backend'] = 'MySQL';
        }
            $this->loadBackend($this->conf['backend']);
        
        // daemon
        if(!array_key_exists('daemon',$this->conf)) {
        	$this->conf['daemon'] = 'Default';
        }
            $this->loadDaemon($this->conf['daemon']);
        
        // runner
        if(!array_key_exists('runner',$this->conf)) {
        	$this->conf['runner'] = 'SingleThreaded';
        }
            $this->loadRunner($this->conf['runner']);
        
        return $this;
    }
	
    // {{{ setLogger()

    /**
     * Sets the underlying logger
     * 
     * @param  LightJob_ILogger $logger the logger
     * @return LightJob           $this
     */
    public function setLogger(LightJob_ILogger $logger)
    {
        $this->logger = $logger;
        $this->logger->configure($this->conf);
        return $this;
    }

    // }}}
    // {{{ getLogger()

    /**
     * Returns the underlying logger
     *
     * @return LightJob_ILogger the underlying logger
     */
    public function getLogger()
    {
        return $this->logger;
    }

    // }}}
    
    /**
     * Returns the daemon
     *
     * @return LightJob_IDaemon
     */
    public function getDaemon()
    {
    	return $this->daemon;
    }
    
    /**
     * Sets the daemon
     *
     * @param LightJob_IDaemon $daemon
     */
    public function setDaemon(LightJob_IDaemon $daemon)
    {
    	$this->daemon = $daemon;
    	$this->daemon->setLightJob($this);
    }

    /**
     * Returns the runner
     *
     * @return LightJob_IRunner
     */
    public function getRunner()
    {
    	return $this->runner;
    }
    
    /**
     * Sets the runner
     *
     * @param LightJob_IRunner $runner
     */
    public function setRunner(LightJob_IRunner $runner)
    {
    	$this->runner = $runner;
    }
    
    protected function loadBackend($name)
    {
    	$className = 'LightJob_Backend_'.$name;
    	if(!class_exists($className))
    	{
    		throw new NotFoundException("Backend driver '$name' does not exist.");
    	}
    	$this->setBackend(new $className());
    }

    protected function loadDaemon($name)
    {
    	$className = 'LightJob_Daemon_'.$name;
    	if(!class_exists($className))
    	{
    		throw new NotFoundException("Daemon driver '$name' does not exist.");
    	}
    	$this->setDaemon(new $className);
    }
    
    protected function loadRunner($name)
    {
    	$className = 'LightJob_Runner_'.$name;
    	if(!class_exists($className))
    	{
    		throw new NotFoundException("Runner driver '$name' does not exist.");
    	}
    	$this->setRunner(new $className());
    }

	/**
     * Returns the backend
     *
     * @return LightJob_IBackend the backend
     */
    public function getBackend()
    {
    	return $this->backend;
    }
    
    /**
     * Sets the backend
     *
     * @param LightJob_IBackend $backend the backend
     */
    public function setBackend(LightJob_IBackend $backend)
    {
    	$this->backend = $backend;
    	$this->backend->configure($this->conf);
    	$this->backend->connect();
    }
    
    public function countPendingJobs()
    {
    	return $this->backend->countPendingJobs();
    }
    
    public function hasPendingJobs()
    {
    	return $this->backend->hasPendingJobs();
    }
    
    public function addJob($job)
    {
    	return $this->backend->addJob($job);
    }
    
    public function clearAllJobs()
    {
    	return $this->backend->clearAllJobs();
    }
	/**
	 * @see LightJob_ILightJob::listPendingJobs()
	 *
	 * @return array
	 */
	public function listPendingJobs()
	{
		return $this->backend->listPendingJobs();
	}
	
	/**
	 * @see LightJob_ILightJob::markJobCompleted()
	 *
	 * @param array $job
	 */
	public function markJobCompleted($job)
	{
		return $this->backend->markJobCompleted($job);
	}
	
	/**
	 * @see LightJob_ILightJob::markJobsCompleted()
	 *
	 * @param array $jobs
	 */
	public function markJobsCompleted($jobs)
	{
		return $this->backend->markJobsCompleted($jobs);
	}
	
	/**
	 * @see LightJob_ILightJob::cleanJobs()
	 *
	 */
	public function cleanJobs()
	{
		return $this->backend->cleanJobs();
	}
	
	/**
	 * @see LightJob_ILightJob::countJobs()
	 *
	 * @return integer
	 */
	public function countJobs()
	{
		return $this->backend->countJobs();
	}
	
	/**
	 * @see LightJob_ILightJob::process()
	 *
	 */
	public function process()
	{
		
		$end = false;
		if($this->countPendingJobs()>0)
		{
			$jobs = $this->listPendingJobs();
			try
			{
				$this->processJobs($jobs);
				$this->markJobsCompleted($jobs);
			}
			catch(EndException $e)
			{
				$toComplete = array();
				foreach($jobs as $job)
				{
					if($job['mode']==='end')
					{
						$toComplete[] = $job;
						break;
					}
					else
					{
						$toComplete[] = $job;
					}
				}
				$this->markJobsCompleted($toComplete);
				$end = true;
			}
		}
		$this->cleanJobs();

		if($end===true)
		{
			throw new EndException();
		}
		return true;
	}
	
	/**
	 * @see LightJob_ILightJob::run()
	 *
	 */
	public function run()
	{
		if($this->daemon===null)
		{
			throw new NullException("No daemon specified");
		}
		return $this->daemon->run();
	}
	
	/**
	 * @see LightJob_ILightJob::processJob()
	 *
	 * @param array $job
	 */
	public function processJob($job)
	{
		return $this->runner->pushJob($job);
	}
	
	/**
	 * @see LightJob_ILightJob::processJobs()
	 *
	 * @param array $jobs
	 */
	public function processJobs($jobs)
	{
		return $this->runner->pushJobs($jobs);
	}

}